package ch.zbw.timeline.service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import ch.zbw.timeline.domain.Holiday;
import ch.zbw.timeline.domain.HolidayFormula;
import ch.zbw.timeline.domain.YearConfig;
import ch.zbw.timeline.repository.HolidayFormulaRepository;
import ch.zbw.timeline.repository.HolidayRepository;
import ch.zbw.timeline.repository.hibernate.HibernateHolidayFormulaRepository;
import ch.zbw.timeline.repository.hibernate.HibernateHolidayRepository;

public class ZbwHolidayService implements HolidayService {


	private HolidayRepository holidayRepository = new HibernateHolidayRepository();
	private HolidayFormulaRepository holidayFormulaRepository = new HibernateHolidayFormulaRepository();

	// private HolidayRepository holidayRepository = new MockHolidayRepository();
	// private HolidayFormulaRepository holidayFormulaRepository = new MockHolidayFormulaRepository();

	/**
	 * Default constructor
	 */
	public ZbwHolidayService() {
	}

	public ZbwHolidayService(HolidayRepository holidayRepository) {
		this.holidayRepository = holidayRepository;
	}

	/* (non-Javadoc)
	 * @see ch.zbw.timeline.service.HolidayService#saveHoliday(ch.zbw.timeline.domain.Holiday)
	 */
	@Override
	public Holiday saveHoliday(Holiday holiday) {
		return holidayRepository.saveHoliday(holiday);
	}

	/* (non-Javadoc)
	 * @see ch.zbw.timeline.service.HolidayService#getHolidaysByYear(ch.zbw.timeline.domain.YearConfig)
	 */
	@Override
	public List<Holiday> getHolidaysByYear(YearConfig yearConfig) {
		List<Holiday> holidayList = new ArrayList<Holiday>();
		List<Holiday> allHolidays = new ArrayList<Holiday>();
		List<HolidayFormula> holidayFormulas = new ArrayList<HolidayFormula>();
		int year = yearConfig.getYear();

		// capture empty years
		if (year != 0) {
			Date easterSunday = yearConfig.getEasterSunday();
			holidayList = holidayRepository.getHolidaysByYear(yearConfig
					.getId());
			holidayFormulas = holidayFormulaRepository.getAllHolidayFormulas();

			// A loop to create the active Holidays that was saved
			for (int i = 0; i < holidayList.size(); i++) {
				int holidayFk = holidayList.get(i).getHolidayFormulaFk();
				HolidayFormula formula = holidayFormulaRepository
						.getHolidayFormulaById(holidayFk);
				Calendar holidayDate = getDateFromFormula(year, easterSunday,
						formula.getFormula());
				holidayList.get(i).setDateOfHoliday(holidayDate);
			}

			// A loop for all holidays in the database
			for (int j = 0; j < holidayFormulas.size(); j++) {
				Holiday newHoliday = new Holiday();
				newHoliday.setYearConfigFk(yearConfig.getId());
				newHoliday.setHolidayFormulaFk(holidayFormulas.get(j).getId());
				newHoliday.setFormula(holidayFormulas.get(j));
				Calendar calendar = getDateFromFormula(year, easterSunday,
						holidayFormulas.get(j).getFormula());
				newHoliday.setDateOfHoliday(calendar);
				allHolidays.add(newHoliday);
			}

			// active flag at the same date
			for (int k = 0; k < allHolidays.size(); k++) {
				for (int l = 0; l < holidayList.size(); l++) {
					Calendar cal1 = allHolidays.get(k).getDateOfHoliday();
					Holiday holi = holidayList.get(l);
					Calendar cal2 = holi.getDateOfHoliday();
					if (cal1.equals(cal2)) {
						Holiday holiday = allHolidays.get(k);
						holiday.setActive(true);
						holiday.setId(holi.getId());
					}
				}
			}

			// sort the array by date
			Collections.sort(allHolidays, new Comparator<Holiday>() {

				@Override
				public int compare(Holiday o1, Holiday o2) {
					return o1.getDateOfHoliday().compareTo(
							o2.getDateOfHoliday());
				}
			});

		}
		return allHolidays;
	}

	/* (non-Javadoc)
	 * @see ch.zbw.timeline.service.HolidayService#createHolidayFormula(java.lang.String, java.lang.String)
	 */
	@Override
	public HolidayFormula createHolidayFormula(String formula,
			String description) {
		HolidayFormula holidayFormula = new HolidayFormula();

		holidayFormula.setDescription(description);
		holidayFormula.setFormula(formula);
		return holidayFormulaRepository.saveHolidayFormula(holidayFormula);
	}

	/* (non-Javadoc)
	 * @see ch.zbw.timeline.service.HolidayService#saveHolidayFormula(ch.zbw.timeline.domain.HolidayFormula)
	 */
	@Override
	public HolidayFormula saveHolidayFormula(HolidayFormula holidayFormula) {
		if (holidayFormulaRepository.saveHolidayFormula(holidayFormula) != null) {
			return holidayFormula;
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see ch.zbw.timeline.service.HolidayService#getAllHolidayFormulas()
	 */
	@Override
	public List<HolidayFormula> getAllHolidayFormulas() {
		return holidayFormulaRepository.getAllHolidayFormulas();
	}

	/**
	 * These methods gets the Date by Formula
	 * 
	 * @param year year of formula
	 * @param easterSunday the easter Sunday from the actually year
	 * @param holidayFormula the name of the formula
	 * @return the calendar
	 */
	private Calendar getDateFromFormula(int year, Date easterSunday,
			String holidayFormula) {

		String yearString = Integer.toString(year);
		StringBuffer buffer = new StringBuffer(holidayFormula);

		if (buffer.substring(buffer.length() - 1).equals("Y")) {

			Calendar calendar = Calendar.getInstance();
			simpleDateMergeYear(calendar, yearString, buffer);
			return calendar;
		}

		if (easterSunday != null) {
			if (buffer.substring(buffer.length() - 1).equals("N")) {

				Calendar calendar = Calendar.getInstance();
				calendar.setTime(easterSunday);
				return calendar;

			} else {
				if (holidayFormula.length() <= 8) {

					Calendar calendar = Calendar.getInstance();
					mergeDateWithShortFormula(easterSunday, calendar, buffer);
					return calendar;

				} else {

					Calendar calendar = Calendar.getInstance();
					mergeDateWithLongFormula(easterSunday, calendar, buffer);
					return calendar;
				}

			}
		}
		Calendar calendar = null;
		return calendar;

	}

	/**
	 * These methods merge the date with the long formula
	 * 
	 * @param easterSunday easter Sunday from year
	 * @param calendar the actually calendar
	 * @param buffer StringBuffer
	 */
	private void mergeDateWithLongFormula(Date easterSunday, Calendar calendar,
			StringBuffer buffer) {
		char c = buffer.charAt(buffer.length() - 3);

		switch (c) {
		case '-':
			int temp = Integer.parseInt(buffer.substring(buffer.length() - 3));
			calendar.setTime(easterSunday);
			calendar.add(Calendar.DATE, temp);
			break;

		case '+':
			int temp1 = Integer.parseInt(buffer.substring(buffer.length() - 2));
			calendar.setTime(easterSunday);
			calendar.add(Calendar.DATE, temp1);
			break;
		default:
			break;
		}
	}

	/**
	 * These methods merge the date with the short formula
	 * 
	 * @param easterSunday easter Sunday from year
	 * @param calendar the actually calendar
	 * @param buffer StringBuffer
	 */
	private void mergeDateWithShortFormula(Date easterSunday,
			Calendar calendar, StringBuffer buffer) {
		char c = buffer.charAt(buffer.length() - 2);

		switch (c) {
		case '-':
			int temp = Integer.parseInt(buffer.substring(buffer.length() - 2));
			calendar.setTime(easterSunday);
			calendar.add(Calendar.DATE, temp);
			break;

		case '+':
			int temp1 = Integer.parseInt(buffer.substring(buffer.length() - 1));
			calendar.setTime(easterSunday);
			calendar.add(Calendar.DATE, temp1);
			break;
		default:
			break;
		}
	}

	/**
	 * These methods merge the date with the year
	 * 
	 * @param easterSunday easter Sunday from year
	 * @param calendar the actually calendar
	 * @param buffer StringBuffer
	 */
	private void simpleDateMergeYear(Calendar calendar, String yearString,
			StringBuffer buffer) {
		buffer = buffer.delete(buffer.length() - 4, buffer.length());
		buffer.append(yearString);
		String dateString = new String(buffer);

		SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");

		Date date = null;
		try {
			date = dateFormat.parse(dateString);
		} catch (ParseException e) {
			System.out.println(e.getMessage());
		}

		calendar.setTime(date);
	}

	/* (non-Javadoc)
	 * @see ch.zbw.timeline.service.HolidayService#deleteHoliday(ch.zbw.timeline.domain.Holiday, ch.zbw.timeline.domain.YearConfig)
	 */
	@Override
	public void deleteHoliday(Holiday h, YearConfig yearConfig) {
			holidayRepository.delete(h, yearConfig.getId());
	}
}










